package dk.dashboardFP.panels.loggerPanel {
	import dk.dashboardFP.core.DashboardFPCore;
	import dk.dashboardFP.dfpLogger.DFPLogItem;
	import dk.dashboardFP.dfpLogger.DFPLogger;
	import dk.dashboardFP.dfpLogger.DFPLoggerEvent;
	import dk.dashboardFP.dfpLogger.DFPLoggerLevels;
	import dk.dashboardFP.dfpLogger.InspectLogItem;
	import dk.dashboardFP.dfpLogger.view.LogItemViewEvent;
	import dk.dashboardFP.panels.introspectionPanel.DCIntrospectionNode;
	import dk.dashboardFP.panels.introspectionPanel.IntrospectionNode;
	import dk.dashboardFP.panels.introspectionPanel.IntrospectionPanel;
	import dk.dashboardFP.panels.panelElements.AbstractDashboardFPPanel;
	import dk.dashboardFP.panels.panelElements.DashboardFPPanelEvent;
	import dk.dashboardFP.utils.DisplayChainTools;
	import dk.dashboardFP.utils.StringUtils;
	import dk.dashboardFP.utils.describeObjectUtil.DescribeObjectUtil;
	import dk.dashboardFP.utils.describeObjectUtil.ObjectDescription;

	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.utils.describeType;

	/**
	 * @author josef
	 */
	public class DFPLoggerPanel extends AbstractDashboardFPPanel {
		private var _oLogWindow : LogWindowSubPanel;
		private var _oFilterWindow : FilterWindowSubPanel;
		private var _sIntrospectionPanelName : String = "Introspection Panel";
		private var _oIntrospectionPanel : IntrospectionPanel;

		private var _a_sPrimitiveTypes : Array = ["string", "number", "boolean", "int", "null"];
		private var _numNodes : int;

		private var _introspectionEscapeSets : Array = [{type:DisplayObject, prop:"stage"}, {type:DisplayObject, prop:"parent"}, {type:DisplayObject, prop:"root"}, {type:DisplayObject, prop:"content"}];
		private var _objectList : Array;

		
		public function DFPLoggerPanel(doDashboardFPCore : DashboardFPCore) {
			super(doDashboardFPCore, "Logger");
			setup();
		}

		override protected function setup() : void {
			super.setup();
			
			//:::::::
			// listen for incomming logs 
			DFPLogger.getInstance().addEventListener(DFPLoggerEvent.LOG, onLog, false, 0, true);
			
			//:::::::
			// setup interactivity logging
			DFPLogger.getLogGroupById(DFPLoggerGroups.INTERACTIVITY_EVENTS).name = "Interactivity Events";	
			DFPLogger.getLogGroupById(DFPLoggerGroups.INTERACTIVITY_EVENTS).color = 0xFF0099;
			DFPLogger.getLogGroupById(DFPLoggerGroups.INTERACTIVITY_EVENTS).logWhileHidden = false;
			
			//:::::::
			// filterwindow setup 
			_oFilterWindow.evaluateGroup(DFPLoggerGroups.DEFAULT);
			_oFilterWindow.evaluateGroup(DFPLoggerGroups.INTERACTIVITY_EVENTS);
			
			//:::::::
			// register global events 
			doDashboard.doStage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyboardEvent, false, 0, true);
			doDashboard.doStage.addEventListener(MouseEvent.CLICK, onMouseEvent, false, 0, true);
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// interactive event handlers
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function onMouseEvent(event : MouseEvent) : void {
			
			//:::::::
			// escape if child of panels 
			if (DisplayChainTools.get_bIsChildOf(DisplayObject(event.target), doDashboard.doPanelHolder)) return;
			DFPLogger.log(event.type + "   " + event.target + "   Ctrl : " + event.ctrlKey + "   Alt : " + event.altKey, DFPLoggerGroups.INTERACTIVITY_EVENTS);
		}

		private function onKeyboardEvent(event : KeyboardEvent) : void {
			DFPLogger.log(event.type + "   Ctrl : " + event.ctrlKey + "   Alt : " + event.altKey + "   CharCode : " + event.charCode + "   KeyCode : " + event.keyCode, DFPLoggerGroups.INTERACTIVITY_EVENTS);
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// log handler
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function onLog(event : DFPLoggerEvent) : void {
			_oFilterWindow.evaluateGroup(event.oDFPLogItem.intGroup);
			_oLogWindow.doLoggerWindowContent.addLog(event.oDFPLogItem);
			
			//:::::::
			// create introspection dump 
			if (event.oDFPLogItem.oData) {
				convertToIntrospectionSnapshot(event.oDFPLogItem);
			}
			
			//:::::::
			// if introspection 
			if (event.oDFPLogItem is InspectLogItem) {
				var inspectionLogItem : InspectLogItem = event.oDFPLogItem as InspectLogItem;
				if (inspectionLogItem.autoOpen) {
					checkAndCreateInspectionPanel();
					launchIntrospectionNode(inspectionLogItem.oData as IntrospectionNode, inspectionLogItem.preSearch);
				}
			}
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// subpanels
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		override protected function createPanelElements() : void {
			
			//:::::::
			// filter list 
			_oFilterWindow = new FilterWindowSubPanel(this, "Filter");
			addSubPanel(_oFilterWindow);
			
			//:::::::
			// log list 
			_oLogWindow = new LogWindowSubPanel(this, "Logs", _oFilterWindow);
			addSubPanel(_oLogWindow);
			
			_oLogWindow.doLoggerWindowContent.addEventListener(LogItemViewEvent.REQUEST_OBJECT_INTROSPECT, onIntrosectionRequest);
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// introspection
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function onIntrosectionRequest(event : LogItemViewEvent) : void {
			doIntrospect(event.target as DataLogItemView);
		}

		private function doIntrospect(logItemView : DataLogItemView) : void {
			checkAndCreateInspectionPanel();
			launchIntrospectionNode(logItemView.oDFPLogItem.oData as IntrospectionNode, ((logItemView.oDFPLogItem is InspectLogItem) ? (logItemView.oDFPLogItem as InspectLogItem).preSearch : ""));
		}

		private function checkAndCreateInspectionPanel() : void {
			if (!_oIntrospectionPanel) {
				_oIntrospectionPanel = new IntrospectionPanel(_oDashboardFB.dashbardFPCore as DashboardFPCore, _sIntrospectionPanelName);
				_oIntrospectionPanel.addEventListener(DashboardFPPanelEvent.KILLING_PANEL, onIntrospectionKilled, false, 0, true);
				
				//:::::::
				// add panel 
				(_oDashboardFB.dashbardFPCore as DashboardFPCore).addTempPanel(_oIntrospectionPanel);
			}
		}

		private function launchIntrospectionNode(introspectionNode : IntrospectionNode, preSearch : String = "") : void {
			_oIntrospectionPanel.introspectObject(introspectionNode, preSearch);
		}

		private function onIntrospectionKilled(event : DashboardFPPanelEvent) : void {
			clearIntrospectionPanel();
		}

		private function clearIntrospectionPanel() : void {
			_oIntrospectionPanel = null;
		}

		private function convertToIntrospectionSnapshot(oDFPLogItem : DFPLogItem) : void {

			_numNodes = 0;
			 
			_objectList = [];
			
			if (oDFPLogItem is InspectDCLogItem) {
				oDFPLogItem.oData = getDCIntrospectionNode(oDFPLogItem.oData as DisplayObject, 0);
			} else {
				oDFPLogItem.oData = get_oIntroSpectionNode(oDFPLogItem.oData, 0);
			}
			
			if (_numNodes >= _oDashboardSettings.numIntrospectionNodesProp.nValue) {
				DFPLogger.log("Exeeded maximum number of nodes in introspection (" + _oDashboardSettings.numIntrospectionNodesProp.nValue+ ")", -1, null, DFPLoggerLevels.WARN);
			}

			_objectList = null;
		}

		private function get_oIntroSpectionNode(oObject : Object, intDepth : int, sPropName : String = "") : IntrospectionNode {
			
			var od : ObjectDescription = DescribeObjectUtil.getObjectDescription(oObject);			
			
			//:::::::
			// create new node 
			var oNode : IntrospectionNode = new IntrospectionNode(intDepth, od);
			
			oNode.sValue = "";
			if (_a_sPrimitiveTypes.indexOf(od.className.toLowerCase()) != -1) {
				oNode.sValue = getValueStringFromObject(oObject, sPropName);
				
				//:::::::
				// retun since object type is primitive and therefore has no children 
				return oNode;
			}
			
			//:::::::
			// safty
			_numNodes++; 
			if (_numNodes > _oDashboardSettings.numIntrospectionNodesProp.nValue) {
				oNode.sValue = wrapGrey(sPropName + " (Max nodes exeeded)");;
				return oNode;
			}
			
			//:::::::
			// check if already node-ifyed..  
			if (_objectList.indexOf(oObject) != -1) {
				oNode.sValue = wrapGrey(sPropName + " (Duplicate refrence)");
				return oNode;
			} else {
				_objectList[_objectList.length] = oObject;
			}
			
			//:::::::
			// check for escapes
			for each (var escapeSet : Object in _introspectionEscapeSets) {
				if (oObject is escapeSet.type && sPropName == escapeSet.prop) {
					oNode.sValue = wrapGrey(sPropName + " (escaped recursion)");
					return oNode;
				}
			}
			
			// check if dynamic type and loop through dynamic props 
			if (od.isDynamic) {
				
				if (oNode.sValue == "") {
					oNode.sValue = (sPropName != null && sPropName != "" ? wrapGrey(sPropName + " : ") : "") + od.className;;
				} 
					
				for (var s : String in oObject) {
					oNode.addSubNode(get_oIntroSpectionNode(oObject[s], oNode.depth + 1, s));
				}
			}
			
			
			//:::::::
			// switch subnode 
			switch(od.className) {
				case "Object":
					/*
					if (oNode.sValue == "") {
					oNode.sValue = od.className;
					} 
					
					//:::::::
					// subnodes
					for (var s : String in oObject) {
					oNode.addSubNode(get_oIntroSpectionNode(oObject[s], oNode.depth + 1, s));
					}
					
					return oNode;
					 * 
					 */
					break;
				
				case "Array":
					
					//:::::::
					// subnodes
					/*
					if (oNode.sValue == "") {
					oNode.sValue = od.className + (sPropName != null ? sPropName : "");
					} 
					for each (var oSubObject : Object in oObject) {
					oNode.addSubNode(get_oIntroSpectionNode(oSubObject, oNode.depth + 1, (oObject as Array).indexOf(oSubObject).toString()));
					}
					 * 
					 */
					/*
					var oSubObject : Object;
					for (var i : Number = 0;i < oObject.length;i++) {
					oSubObject = oObject[i];
					oNode.addSubNode(get_oIntroSpectionNode(oSubObject, oNode.depth + 1, (oObject as Array).indexOf(oSubObject).toString()));
					}
					 * 
					 */

					return oNode;
					break;
				case "XML":

					oNode.sValue = getValueStringFromXML(oObject as XML);
					
					//:::::::
					// subnodes
					for each (var xmlNode : XML in oObject.children()) {
						if (xmlNode.name() != undefined) {
							oNode.addSubNode(get_oIntroSpectionNode(xmlNode, oNode.depth + 1));
						}
					}
					
					return oNode;
					break;
				case "XMLList":

					oNode.sValue = "XMLList";
					
					//:::::::
					// subnodes
					for (var i : Number = 0;i < XMLList(oObject).length();i++) {
						oNode.addSubNode(get_oIntroSpectionNode(oObject[i], oNode.depth + 1));
					}
					
					return oNode;
					break;
			}
			
			//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
			// custom object
			//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

			var objectDescription : XML = describeType(oObject);
			oNode.sValue = getValueStringFromObject(od.className, sPropName);
			
			//:::::::
			// get public getters
			for each (var accessor : XML in objectDescription.accessor) {
				try {
					oNode.addSubNode(get_oIntroSpectionNode(oObject[accessor.@name], oNode.depth + 1, accessor.@name));
				}catch(error : Error) {
				}
			}
			
			//:::::::
			// variables
			for each (var variable : XML in objectDescription.variable) {
				oNode.addSubNode(get_oIntroSpectionNode(oObject[variable.@name], oNode.depth + 1, variable.@name));
			} 
			
			//:::::::
			// return  
			return oNode;
		}

		private function getValueStringFromXML(nodeXML : XML) : String {
			
			var sReturn : String = wrapGrey("&#60;") + nodeXML.name() + "";
			sReturn += wrapGrey(" ");
			for each(var a:XML in nodeXML.@*) {
				sReturn += (wrapGrey(" " + a.name() + " = ") + a.toXMLString() + " ");
			}
			if (nodeXML.text() != undefined) {
				if (nodeXML.children().length() != 0) {
					sReturn += wrapGrey("&#62;");
					sReturn += nodeXML.text().toString().split("<").join("&#60;").split(">").join("&#62;");
					sReturn += wrapGrey("&#60;&#47;") + nodeXML.name() + wrapGrey("&#62;");
				} else {
					sReturn += wrapGrey(" &#47;&#62;");
				}
			} else {
				sReturn += wrapGrey(" &#47;&#62;");
			}
			
			return sReturn;
		}

		private function getValueStringFromObject(oObject : *, sPropName : String) : String {
				
			var sReturnString : String = "";
			sReturnString += wrapGrey(sPropName + ((sPropName != "") ? " : " : "")) + oObject;
			
			return wrapBasicFormat(sReturnString);
		}

		private function wrapBasicFormat(sReturnString : String) : String {
			return sReturnString;
		}

		private function wrapGrey(sPropName : String) : String {
			return "<font color='#999999'>" + sPropName + "</font>";
		}

		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
		// display chain introspection
		//::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

		private function getDCIntrospectionNode(displayObject : DisplayObject, depth : int) : IntrospectionNode {

			if ((displayObject is DashboardFPCore)) return null;  
			
			var node : DCIntrospectionNode = new DCIntrospectionNode(depth, DescribeObjectUtil.getObjectDescription(displayObject), displayObject);
			node.sValue = StringUtils.get_sObjectName(displayObject);

			if (displayObject.name) {
				if (displayObject.name.indexOf("instance") == -1) {
					node.sValue += wrapGrey(" : " + displayObject.name);
				}
			}

			_numNodes++;
			//:::::::
			// safty
			
			_numNodes++; 
			if (_numNodes > _oDashboardSettings.numIntrospectionNodesProp.nValue) {
				node.sValue += wrapGrey(" (Max nodes exeeded)");;
				return node;
			}
			
			if (displayObject is DisplayObjectContainer) {
				for (var i : Number = 0;i < DisplayObjectContainer(displayObject).numChildren;i++) {
					
					try {
						var subNode : IntrospectionNode = getDCIntrospectionNode(DisplayObjectContainer(displayObject).getChildAt(i), depth + 1);
						if(subNode) {
							node.addSubNode(subNode);
						}
					}catch(error : Error) {
					}
				}
			}
			
			return node;
		}
	}
}
